package com.francis.processor;

import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.lang.annotation.Annotation;
import java.util.Set;

/**
 * @author: francis
 * @description:
 * @date: 2021/4/11 17:15
 */
@SupportedAnnotationTypes(
value = {
    "org.springframework.scheduling.annotation.Async",
    "org.springframework.transaction.annotation.Transactional"
})
//@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class AnnotationVerifyProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        processingEnv.getMessager().printMessage(
                Diagnostic.Kind.NOTE, "校验注解 {@Transactional, @Async} 是否使用正确...");
        verifyAopAnnotation(roundEnv, Transactional.class);
        verifyAopAnnotation(roundEnv, Async.class);
        return true;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 校验 @Transactional, @Async 这一类和Spring Aop相关的注解
     * @param roundEnv
     * @param clazz
     */
    private void verifyAopAnnotation(RoundEnvironment roundEnv, Class<? extends Annotation> clazz) {
        final Messager messager = processingEnv.getMessager();
        String annotationName = clazz.getSimpleName();
        for (Element element : roundEnv.getElementsAnnotatedWith(clazz)) {
            final ElementKind kind = element.getKind();
            if (!(ElementKind.METHOD == kind)) {
                // 打印 WARN 级别日志，只是建议，并不强制
                messager.printMessage(Diagnostic.Kind.WARNING,
                        "建议：@" + annotationName + " 不要加在类上，尽量精确到方法。当前类：" + element);
            } else {
                for (Modifier modifier : element.getModifiers()) {
                    if (!(Modifier.PUBLIC == modifier)) {
                        // 打印 ERROR 级别日志，强制编译失败
                        messager.printMessage(Diagnostic.Kind.ERROR,
                                "警告：@" + annotationName + " 只在public修饰的方法上生效。当前方法：" + element);
                    }
                }
            }
        }
    }

}
